package org.screenlock.util;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Stack;

/**
 * @author xuhuize
 */
public class ExpressUtil {
    public static final String      matchNumber   = "\\p{Digit}++\\.?+\\p{Digit}*+";       // 常数
    public static final String      matchVariable = "[\\p{Alpha}|\\#|\\_|\\.]*";           // 非常数
    public static final String      matchLeft     = "\\(";                                 // 左括号
    public static final String      matchRight    = "\\)";                                 // 右括号
    public static final String      matchOperator = "\\(|\\)|\\+|\\-|\\*|\\/|\\p{Alpha}++"; // 操作符
    public static final String      matchOperand  = "\\p{Digit}++\\.?+\\p{Digit}*+|\\#.++"; // 操作数
    public static final String      matchAnS      = "\\+|\\-";                             // 加减
    public static final String      matchMnD      = "\\*|\\/";                             // 乘除
    public static final String      matchFunction = "[\\p{Alpha}]++";                      // 函数
    public static final String      matchPoint    = "\\.";                                 // 小数点
    private static final Properties properties    = new Properties();
    static {
        try {
            /*
             * StackTraceElement[] stack=Thread.currentThread().getStackTrace();
             * String packPath=
             * Class.forName(stack[1].getClassName()).getPackage
             * ().getName().replace('.', '/');
             * properties.load(Class.forName(stack[1].getClassName())
             * .getResourceAsStream("/"+packPath+"/method.properties"));
             */
            properties.load(Thread.currentThread().getContextClassLoader()
                    .getResourceAsStream("method.properties"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * @param s
     * @return <p>
     *         将字符串表达式转换成前缀表达式
     *         </p>
     */
    public static List<String> toPrefix(String s) {
        return step2(step1(s));
    }

    /**
     * @param express
     * @return <p>
     *         第一步:将操作数和操作符分开 如 "12+3" --> {"12","+","3"}
     *         </p>
     */
    private static Stack<String> step1(String express) {
        Stack<String> result = new Stack<String>();
        char[] chars = express.toCharArray();
        for (char c : chars) {
            String temp = Character.toString(c);
            if (result.isEmpty()) {
                result.push(temp);
            } else {
                if (temp.matches(matchPoint)) {
                    String top = result.peek();
                    top += temp;
                    result.pop();
                    result.push(top);
                    continue;
                } else if (temp.matches(matchNumber)) {
                    String top = result.peek();
                    if (top.matches(matchNumber)) {
                        top += temp;
                        result.pop();
                        result.push(top);
                    } else {
                        result.push(temp);
                    }
                } else if (temp.matches(matchVariable)) {
                    String top = result.peek();
                    if (top.matches(matchVariable)) {
                        top += temp;
                        result.pop();
                        result.push(top);
                    } else {
                        result.push(temp);
                    }
                } else {
                    result.add(temp);
                }
            }
        }
        return result;
    }

    /**
     * @param express
     * @return <p>
     *         第二步:转换成前缀表达式
     *         </p>
     */
    private static List<String> step2(Stack<String> express) {
        Stack<String> temp = new Stack<String>();
        List<String> result = new ArrayList<String>();
        while (express.isEmpty() == false) {
            String e = express.pop();
            if (e.matches(matchOperand)) {// 操作数
                result.add(e);
            } else if (e.matches(matchOperator)) {// 操作符
                if (e.matches(matchRight)) { // 右括号
                    temp.push(e);
                } else if (e.matches(matchLeft)) {// 左括号
                    while (temp.isEmpty() == false) {
                        String top = temp.pop();
                        if (top.matches(matchRight)) {
                            break;
                        } else {
                            result.add(top);
                        }
                    }
                } else {// 四则运算或者函数
                    if (temp.isEmpty()) {// 栈空直接入栈
                        temp.push(e);
                    } else {// 比较优先级
                        if (temp.peek().matches(matchLeft) | temp.peek().matches(matchRight)) {// 栈顶是括号的话直接入栈
                            temp.push(e);
                        } else {// 栈顶不是括号
                            while (true) {
                                if (temp.isEmpty() == true) {
                                    temp.push(e);
                                    break;
                                }
                                String top = temp.peek();
                                if (highter(e, top)) {
                                    temp.push(e);
                                    break;
                                } else {
                                    result.add(temp.pop());
                                }
                            }
                        }
                    }
                }
            }
        }
        while (temp.isEmpty() == false) {// 最后一步
            String top = temp.pop();
            result.add(top);
        }
        /*
         * int i=0; String[] prefix=new String[result.size()];
         * while(result.isEmpty()==false){ prefix[i++]=result.pop(); }
         */
        return result;
    }

    /**
     * @param a
     * @param b
     * @return <p>
     *         比较操作符的优先级别
     *         </p>
     */
    private static boolean highter(String a, String b) {
        boolean result = false;
        if (getPriority(a) >= getPriority(b)) {
            result = true;
        } else {
            result = false;
        }
        return result;
    }

    /**
     * @param operator
     * @return <p>
     *         获得操作符的优先级别
     *         </p>
     */
    private static int getPriority(String operator) {
        int priority = 0;
        if (operator.matches(matchAnS)) {
            priority = 0;
        } else if (operator.matches(matchMnD)) {
            priority = 1;
        } else if (operator.matches(matchFunction)) {
            priority = 1;
        }
        return priority;
    }

    /**
     * @param express
     *            字符串表达式
     * @return 表达式的计算结果
     */
    public static Double eval(String express) throws Exception {
        List<String> prefix = toPrefix(express);
        Stack<String> temp = new Stack<String>();
        int i = 0;
        for (String top : prefix) {
            if (top.matches(matchOperand)) {
                temp.push(top);
            } else if (top.matches(matchOperator)) {
                if (temp.isEmpty()) {
                    temp.push(top);
                } else {
                    Double value = cal(top, temp);
                    temp.push(value.toString());
                }
            }
        }
        return Double.valueOf(temp.pop());
    }

    /**
     * @param operator
     *            操作符
     * @param args
     *            存放结果的栈
     * @return
     */
    private static double cal(String operator, Stack<String> args) throws Exception {
        double result = 0.0f;
        if (operator.equals("+")) {
            result = Double.valueOf(args.pop()) + Double.valueOf(args.pop());

        } else if (operator.equals("-")) {
            result = Double.valueOf(args.pop()) - Double.valueOf(args.pop());

        } else if (operator.equals("*")) {
            result = Double.valueOf(args.pop()) * Double.valueOf(args.pop());

        } else if (operator.equals("/")) {
            result = Double.valueOf(args.pop()) / Double.valueOf(args.pop());

        } else {
            String value = properties.getProperty(operator);
            String[] values = value.split(" ");
            String className = values[0].substring(0, value.lastIndexOf("."));
            String methodName = values[0].substring(value.lastIndexOf(".") + 1);
            int argSize = Integer.parseInt(values[1]);
            try {
                Class math = Class.forName(className);
                Object proxy = null;
                try {
                    proxy = math.newInstance();
                } catch (Exception e) {
                    proxy = null;
                }
                List<Class> a = new ArrayList<Class>();
                List<Double> b = new ArrayList<Double>();
                for (int i = 0; i < argSize; i++) {
                    a.add(double.class);
                    b.add(Double.valueOf(args.pop()));
                }
                Class[] a_temp = new Class[argSize];
                Method method = math.getMethod(methodName, a.toArray(a_temp));
                result = (Double) method.invoke(proxy, b.toArray());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return result;
    }
}
